#include <hd_led.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled_protocol_spi.h>
#include <hd_motor.h>
#include <pd_i2c_hd.h>
#include <hd_flash.h>
#include "main.h"


#define OLED_PROTOCOL       2   // Select a protocol. SPI, 1; I2C, 2;
#define OLED_PROTOCOL_IMPL  2   // Select a implement. simulate, 1; hardware, 2;

static LED *led;
static OLED *oled;

void oled_instance();

extern void SystemClock_Config(void);


extern void SystemClock_Config(void);

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    GPIO *ledGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_2);
    led = LEDBuilder.build(ledGPIO);
    led->init(led);

    oled_instance();

    oled->showString(oled, 0, 0, "TIM PWM MOTOR");
    oled->showString(oled, 0, 16, "Speed:");
    oled->refresh(oled);

    // PWMA: PC6 -- AF2 -- TIM3_CH1
    GPIO *motor1GPIO[] = {GPIOBuilder.build(GPIOC, LL_GPIO_PIN_6)};
    motor1GPIO[0]->Mode = LL_GPIO_MODE_ALTERNATE;
    motor1GPIO[0]->Alternate = LL_GPIO_AF_2;
    uint32_t motorChannel1[] = {LL_TIM_CHANNEL_CH1};
    TimerCh *motor1TimerChannel = TimerChBuilder.build(motor1GPIO, 1, motorChannel1, 1);

    // Timer Base Unit: freq = 1kHz, duty is variable, resolution = 1%
    Timer *motor1Timer = TimerBuilder.build(TIM3, 84 - 1, 100 - 1);
    TimerOC *motor1TimerOC = TimerOCBuilder.build(motor1Timer, motor1TimerChannel, 0);
    TimerPWM *motor1TimerPWM = TimerPWMBuilder.build(motor1Timer, motor1TimerOC);
    motor1TimerPWM->init(motor1TimerPWM);

    // AIN1, AIN2
    GPIO *aIn1 = GPIOBuilder.build(GPIOE, LL_GPIO_PIN_3);
    GPIO *aIn2 = GPIOBuilder.build(GPIOE, LL_GPIO_PIN_4);
    Motor *motor1 = MotorBuilder.build(motor1TimerPWM, aIn1, aIn2);
    motor1->init(motor1);
    motor1->start(motor1);


    // PWMB: PD12 -- AF2 -- TIM4_CH1
    GPIO *motor2GPIO[] = {GPIOBuilder.build(GPIOD, LL_GPIO_PIN_12)};
    motor2GPIO[0]->Mode = LL_GPIO_MODE_ALTERNATE;
    motor2GPIO[0]->Alternate = LL_GPIO_AF_2;
    uint32_t motor2Channel[] = {LL_TIM_CHANNEL_CH1};
    TimerCh *motor2TimerChannel = TimerChBuilder.build(motor2GPIO, 1, motor2Channel, 1);

    // Timer Base Unit: freq = 1kHz, duty is variable, resolution = 1%
    Timer *motor2Timer = TimerBuilder.build(TIM4, 84 - 1, 100 - 1);
    TimerOC *motor2TimerOC = TimerOCBuilder.build(motor2Timer, motor2TimerChannel, 0);
    TimerPWM *motor2TimerPWM = TimerPWMBuilder.build(motor2Timer, motor2TimerOC);
    motor2TimerPWM->init(motor2TimerPWM);

    // BIN1, BIN2
    GPIO *bIn1 = GPIOBuilder.build(GPIOE, LL_GPIO_PIN_5);
    GPIO *bIn2 = GPIOBuilder.build(GPIOE, LL_GPIO_PIN_6);
    Motor *motor2 = MotorBuilder.build(motor2TimerPWM, bIn1, bIn2);
    motor2->init(motor2);
    motor2->start(motor2);


    int8_t speed = 0;
    uint16_t delay = 3000;

    oled->showSignedNum(oled, 48, 16, speed, 4);
    oled->refresh(oled);
    delay_ms(delay);

    while (1) {
        speed += 20;
        speed = speed > 100 ? -100 : speed;
        oled->showSignedNum(oled, 48, 16, speed, 4);
        oled->refresh(oled);

        motor1->setSpeed(motor1, speed);
        motor2->setSpeed(motor2, speed);
        delay_ms(delay);
    }

}

void oled_instance() {
#if OLED_PROTOCOL == 1
    GPIO *sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    GPIO *resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO *dcGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO *csGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2);  // Chip Select(Slave Select), General IO
    SPIGPIO *spiGPIO = SPIGPIOBuilder.build(&csGPIO, 1, sckGPIO, sdaGPIO, NULL);

#if OLED_PROTOCOL_IMPL == 1
    SPISimulate *spiObj = SPISimulateBuilder.build(spiGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    SPIHardware *spiObj = SPIHardwareBuilder.build(SPI1, spiGPIO);
#endif
    SPI *spi1 = (SPI *) spiObj;
    spi1->init(spi1);
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi1, resGPIO, dcGPIO, csGPIO);

#elif OLED_PROTOCOL == 2
    GPIO *sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#if OLED_PROTOCOL_IMPL == 1
    I2CSimulate *i2CObj = I2CSimulateBuilder.build(sckGPIO, sdaGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    I2CHardware *i2CObj = I2CHardwareBuilder.build(I2C2, sckGPIO, sdaGPIO);
#endif
    I2C *i2c = (I2C *) i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    StorageFlash *storageFlash = StorageFlashBuilder.build();
    OLEDFont *oledFont = OLEDFontBuilder.build((Storage *) storageFlash, ENCODING_UTF8);
    oled = OLEDBuilder.build((OLEDProtocol *) oledProtocolObj, oledFont);
    oled->init(oled);
}
